<template>
  <div class="visualizer-container">
    <div class="visualizer-content">
      <!-- 只保留组件内部的正则输入框 -->
      <div class="visualizer-regex-input code">
        <table>
          <tbody>
          <tr>
            <td style="width:1em">/</td>
            <td>
              <input
                  v-model="regexInput"
                  class="input"
                  @keyup="onKeyup"
                  @keydown="onEnter"
                  placeholder="输入正则表达式"
              />
            </td>
            <td style="width:1em">/</td>
            <td style="width:3em">{{ flags }}</td>
          </tr>
          </tbody>
        </table>
      </div>

      <!-- 标志选项 -->
      <div class="visualizer-flag-options">
        <label class="visualizer-flag-label">
          <input type="checkbox" v-model="flagI" @change="onFlagChange"/>IgnoreCase
        </label>
        <label class="visualizer-flag-label">
          <input type="checkbox" v-model="flagM" @change="onFlagChange"/>Multiline
        </label>
        <label class="visualizer-flag-label">
          <input type="checkbox" v-model="flagG" @change="onFlagChange"/>GlobalMatch
        </label>
      </div>
      <!-- 错误信息 -->
      <p v-if="errorMessage" class="visualizer-error-box code">{{ errorMessage }}</p>
      <!-- 可视化图形容器 -->
      <div ref="graphContainer" class="visualizer-graph-container code"></div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, nextTick, watch } from 'vue';
import regulexScript from '../assets/regulex.js?url';

const props = defineProps<{ pattern: string }>();
const emit = defineEmits(['update:pattern']);

// 响应式数据
const regexInput = ref(props.pattern || '^(a|b)*?$');
const flagI = ref(false);
const flagM = ref(false);
const flagG = ref(false);
const errorMessage = ref('');
const graphContainer = ref<HTMLElement | null>(null);
const paper = ref<any>(null);
const regulexDeps = ref<any>(null);
const params = ref({
  embed: false,
  re: '',
  highlight: true,
  flags: '',
  debug: false
});

// 计算属性
const flags = computed(() => {
  let result = '';
  if (flagI.value) result += 'i';
  if (flagM.value) result += 'm';
  if (flagG.value) result += 'g';
  return result;
});

// 双向绑定：输入框变化时 emit
watch(regexInput, (val) => {
  emit('update:pattern', `/${val}/${flags.value}`);
  visualize();
});
// 父组件 pattern 变化时同步到输入框
watch(() => props.pattern, (val) => {
  if (val !== regexInput.value) parseFullRegexString(val);
});

// 工具函数
function trim(s: string) {
  return s.replace(/^\s+/, '').replace(/\s+$/, '');
}

// 辅助函数：从完整的正则表达式字符串（如 /regex/flags）中解析标志并更新状态
function parseFullRegexString(fullString: string) {
  const match = fullString.match(/^\/(.*)\/([gimsuy]*)$/);
  if (match) {
    regexInput.value = match[1];
    const flagsStr = match[2];
    flagI.value = flagsStr.includes('i');
    flagM.value = flagsStr.includes('m');
    flagG.value = flagsStr.includes('g');
  } else {
    // 如果不是 /.../flags 格式，则清除标志并返回原始字符串
    regexInput.value = fullString;
    flagI.value = false;
    flagM.value = false;
    flagG.value = false;
  }
}

function getParams() {
  const hash = location.hash;
  if (!hash || hash.length < 2) {
    return { embed: false, re: "", highlight: true, flags: '' };
  }

  const paramsStr = hash.slice(2);
  const paramsObj = paramsStr.split("&").reduce((p: any, a) => {
    const parts = a.split("=");
    p[parts[0]] = parts[1];
    return p;
  }, {});

  return {
    embed: paramsObj.embed === 'true',
    flags: paramsObj.flags || '',
    re: paramsObj.re ? trim(decodeURIComponent(paramsObj.re)) : '',
    debug: paramsObj.debug === 'true',
    cmd: paramsObj.cmd || ''
  };
}

function serializeHash() {
  const re = trim(regexInput.value);
  const flagsStr = flags.value;
  return "#!" +
      (params.value.debug ? "debug=true&" : "") +
      (params.value.embed ? "embed=true&" : "") +
      "flags=" + flagsStr + "&re=" + encodeURIComponent(re);
}

function changeHash() {
  location.hash = serializeHash();
}

function hideError() {
  errorMessage.value = '';
}

function showError(re: string, err: any) {
  let msg = ["Error:" + err.message, ""];
  if (typeof err.lastIndex === 'number') {
    msg.push(re);
    msg.push('-'.repeat(err.lastIndex) + "^");
  }
  errorMessage.value = msg.join("\n");
}

// 获取 regulex 依赖
function getRegulexDependencies() {
  try {
    if (typeof window !== 'undefined') {
      if ((window as any).require) {
        try {
          const regulex = (window as any).require('regulex');
          if (regulex) {
            return {
              parse: regulex.parse,
              visualize: regulex.visualize,
              Raphael: regulex.Raphael,
              Kit: regulex.Kit
            };
          }
        } catch (e) {
          console.error('Require method failed:', e);
        }
      }
    }
    return null;
  } catch (error) {
    console.error('Failed to load Regulex dependencies:', error);
    return null;
  }
}

// 动态加载 regulex 脚本
function loadRegulexScript(): Promise<any> {
  return new Promise((resolve, reject) => {
    if ((window as any).regulexLoaded) {
      const deps = getRegulexDependencies();
      if (deps) {
        resolve(deps);
      } else {
        reject(new Error('Regulex not found after previous load'));
      }
      return;
    }

    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = regulexScript;

    script.onload = () => {
      (window as any).regulexLoaded = true;
      const deps = getRegulexDependencies();
      if (deps) {
        resolve(deps);
      } else {
        reject(new Error('Failed to load Regulex dependencies'));
      }
    };

    script.onerror = () => {
      reject(new Error('Failed to load Regulex script'));
    };

    document.head.appendChild(script);
  });
}

// 核心可视化函数
function visualize(skipError = false) {
  if (!regulexDeps.value) {
    console.error('Regulex dependencies not available');
    return false;
  }

  if (!graphContainer.value) {
    console.error('Graph container not available');
    return false;
  }

  const re = trim(regexInput.value);
  changeHash();
  hideError();

  try {
    // 只在第一次创建 paper，后续重复使用
    if (!paper.value) {
      paper.value = regulexDeps.value.Raphael(graphContainer.value, 10, 10);
    }

    // 解析正则表达式
    const ast = regulexDeps.value.parse(re);

    // 可视化
    regulexDeps.value.visualize(ast, flags.value, paper.value);
    return true;
  } catch (e: any) {
    if (!skipError) {
      showError(re, e);
    }
    return false;
  }
}

// 事件处理函数
function onFlagChange() {
  visualize();
  changeHash();
}

function onKeyup(e: KeyboardEvent) {
  if (e.keyCode === 13) return; // Enter
  clearTimeout((window as any).onKeyupTid);
  (window as any).onKeyupTid = setTimeout(() => {
    visualize();
  }, 100);
}

function onEnter(e: KeyboardEvent) {
  if (e.keyCode === 13) {
    e.preventDefault();
    e.stopPropagation();
    visualize();
  }
}

// 拖拽功能
function dragGraph(container: HTMLElement) {
  if (!container) return;

  container.addEventListener('mousedown', startMove);

  function startMove(e: MouseEvent) {
    clearSelect();
    let x = e.clientX, y = e.clientY;
    container.addEventListener('mousemove', onMove);

    document.addEventListener('mouseup', unbind, true);
    window.addEventListener('mouseup', unbind, true);

    function unbind(e: Event) {
      container.removeEventListener('mousemove', onMove);
      document.removeEventListener('mouseup', unbind, true);
      window.removeEventListener('mouseup', unbind, true);
    }

    function onMove(e: MouseEvent) {
      let dx = x - e.clientX, dy = y - e.clientY;
      if (dx > 0 && container.scrollWidth - container.scrollLeft - container.clientWidth < 2
          || dx < 0 && container.scrollLeft < 1) {
        document.documentElement.scrollLeft += dx;
        document.body.scrollLeft += dx;
      } else {
        container.scrollLeft += dx;
      }
      if (dy > 0 && container.scrollHeight - container.scrollTop - container.clientHeight < 2
          || dy < 0 && container.scrollTop < 1) {
        document.documentElement.scrollTop += dy;
        document.body.scrollTop += dy;
      } else {
        container.scrollTop += dy;
      }
      x = e.clientX;
      y = e.clientY;
    }
  }
}

function clearSelect() {
  if (window.getSelection) {
    if ((window.getSelection() as any).empty) {
      (window.getSelection() as any).empty();
    } else if (window.getSelection()?.removeAllRanges) {
      window.getSelection()?.removeAllRanges();
    }
  } else if ((document as any).selection) {
    (document as any).selection.empty();
  }
}

// 组件挂载时初始化
onMounted(async () => {
  try {
    // 获取 regulex 依赖
    regulexDeps.value = getRegulexDependencies();

    // 如果同步加载失败，尝试异步加载
    if (!regulexDeps.value) {
      regulexDeps.value = await loadRegulexScript();
    }

    if (!regulexDeps.value) {
      errorMessage.value = 'Error: Failed to load Regulex dependencies';
      return;
    }

    // 获取URL参数
    const urlParams = getParams();
    Object.assign(params.value, urlParams);

    // 设置初始值，优先使用 props 中的值，并解析标志
    if (props.pattern) {
      // parseFullRegexString 会同时更新标志状态
      parseFullRegexString(props.pattern);
    } else if (params.value.re) {
      parseFullRegexString(params.value.re);
    }

    // 如果 URL 参数中明确指定了 flags，则覆盖之前解析到的标志
    if (params.value.flags) {
      flagI.value = params.value.flags.includes('i');
      flagM.value = params.value.flags.includes('m');
      flagG.value = params.value.flags.includes('g');
    }

    // 等待 DOM 渲染完成后再初始化
    await nextTick();
    visualize();
    if (graphContainer.value) {
      dragGraph(graphContainer.value);
    }
  } catch (err) {
    errorMessage.value = 'Error: Failed to load Regulex dependencies';
    console.error('Failed to initialize RegexVisualizer:', err);
  }
});
</script>

<style>
.visualizer-container {
  min-height: 200px;
  padding: 20px;
  background: #303030;
  position: relative
}

.visualizer-content {
  width: 100%
}

.visualizer-regex-input {
  background: #eee;
  font-size: 1.2em;
  line-height: 1.4em;
  color: #333;
  border: 1px solid black;
  padding: 4px;
  font-weight: 700;
  word-break: break-all;
  word-wrap: break-word
}

.visualizer-regex-input table {
  border: none;
  padding: 0;
  margin: 0;
  width: 100%
}

.visualizer-regex-input .input {
  color: #3030c0;
  padding: 0 2px;
  border: none;
  background: #eee;
  font-size: 1.2em;
  line-height: 1.4em;
  height: 1.4em;
  font-weight: 700;
  word-break: break-all;
  word-wrap: break-word;
  margin: 0;
  width: 100%;
  outline: none
}

.visualizer-flag-options {
  margin-top: 8px
}

.visualizer-flag-label {
  color: #fff;
  cursor: pointer;
  display: inline-block;
  margin-right: 15px;
  font-size: 14px
}

.visualizer-flag-label input {
  margin-right: 4px
}

.visualizer-error-box {
  background: #eee;
  font-size: 1.2em;
  line-height: 1.4em;
  border: 1px solid black;
  padding: 4px;
  color: #8b0000;
  white-space: pre;
  word-wrap: normal;
  word-break: keep-all;
  overflow: auto;
  margin: 8px 0
}

.visualizer-graph-container {
  padding: 4px;
  margin: 8px 0;
  border: 1px solid black;
  background: #eee;
  overflow: auto;
  cursor: move;
  min-height: 200px;
  border-radius: 0 0 10px 10px;
  display: flex;
  justify-content: center;
  align-items: flex-start
}

.visualizer-graph-container svg {
  display: block;
  margin: 0 auto;
  max-width: 100%;
  height: auto
}
</style>